Ext.lib.Event = function()
{
	var loadComplete = false, unloadListeners = {}, retryCount = 0, onAvailStack = [], _interval, locked = false, win = window, doc = document,

	// constants
	POLL_RETRYS = 200, POLL_INTERVAL = 20, TYPE = 0, FN = 1, OBJ = 2, ADJ_SCOPE = 3, SCROLLLEFT = 'scrollLeft', SCROLLTOP = 'scrollTop', UNLOAD = 'unload', MOUSEOVER = 'mouseover', MOUSEOUT = 'mouseout',
	// private
	doAdd = function()
	{
		var ret;
		if( win.addEventListener )
		{
			ret = function( el, eventName, fn, capture )
			{
				if( eventName == 'mouseenter' )
				{
					fn = fn.createInterceptor(checkRelatedTarget);
					el.addEventListener(MOUSEOVER, fn, (capture));
				}
				else if( eventName == 'mouseleave' )
				{
					fn = fn.createInterceptor(checkRelatedTarget);
					el.addEventListener(MOUSEOUT, fn, (capture));
				}
				else
				{
					el.addEventListener(eventName, fn, (capture));
				}
				return fn;
			};
		}
		else if( win.attachEvent )
		{
			ret = function( el, eventName, fn, capture )
			{
				el.attachEvent("on" + eventName, fn);
				return fn;
			};
		}
		else
		{
			ret = function()
			{
			};
		}
		return ret;
	}(),
	// private
	doRemove = function()
	{
		var ret;
		if( win.removeEventListener )
		{
			ret = function( el, eventName, fn, capture )
			{
				if( eventName == 'mouseenter' )
				{
					eventName = MOUSEOVER;
				}
				else if( eventName == 'mouseleave' )
				{
					eventName = MOUSEOUT;
				}
				el.removeEventListener(eventName, fn, (capture));
			};
		}
		else if( win.detachEvent )
		{
			ret = function( el, eventName, fn )
			{
				el.detachEvent("on" + eventName, fn);
			};
		}
		else
		{
			ret = function()
			{
			};
		}
		return ret;
	}();

	function checkRelatedTarget( e )
	{
		return !elContains(e.currentTarget, pub.getRelatedTarget(e));
	}

	function elContains( parent, child )
	{
		if( parent && parent.firstChild )
		{
			while(child)
			{
				if( child === parent )
				{
					return true;
				}
				child = child.parentNode;
				if( child && (child.nodeType != 1) )
				{
					child = null;
				}
			}
		}
		return false;
	}

	// private
	function _tryPreloadAttach()
	{
		var ret = false, notAvail = [], element, i, v, override, tryAgain = !loadComplete || (retryCount > 0);

		if( !locked )
		{
			locked = true;

			for( i = 0; i < onAvailStack.length; ++i )
			{
				v = onAvailStack[i];
				if( v && (element = doc.getElementById(v.id)) )
				{
					if( !v.checkReady || loadComplete || element.nextSibling || (doc && doc.body) )
					{
						override = v.override;
						element = override ? (override === true ? v.obj : override) : element;
						v.fn.call(element, v.obj);
						onAvailStack.remove(v);
						--i;
					}
					else
					{
						notAvail.push(v);
					}
				}
			}

			retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;

			if( tryAgain )
			{
				startInterval();
			}
			else
			{
				clearInterval(_interval);
				_interval = null;
			}
			ret = !(locked = false);
		}
		return ret;
	}

	// private
	function startInterval()
	{
		if( !_interval )
		{
			var callback = function()
			{
				_tryPreloadAttach();
			};
			_interval = setInterval(callback, POLL_INTERVAL);
		}
	}

	// private
	function getScroll()
	{
		var dd = doc.documentElement, db = doc.body;
		if( dd && (dd[SCROLLTOP] || dd[SCROLLLEFT]) )
		{
			return
			[
			    dd[SCROLLLEFT], dd[SCROLLTOP]
			];
		}
		else if( db )
		{
			return
			[
			    db[SCROLLLEFT], db[SCROLLTOP]
			];
		}
		else
		{
			return
			[
			    0, 0
			];
		}
	}

	// private
	function getPageCoord( ev, xy )
	{
		ev = ev.browserEvent || ev;
		var coord = ev['page' + xy];
		if( !coord && coord !== 0 )
		{
			coord = ev['client' + xy] || 0;

			if( Ext.isIE )
			{
				coord += getScroll()[xy == "X" ? 0 : 1];
			}
		}

		return coord;
	}

	var pub =
	{
		extAdapter: true,
		onAvailable: function( p_id, p_fn, p_obj, p_override )
		{
			onAvailStack.push(
			{
				id: p_id,
				fn: p_fn,
				obj: p_obj,
				override: p_override,
				checkReady: false
			});

			retryCount = POLL_RETRYS;
			startInterval();
		},

		// This function should ALWAYS be called from Ext.EventManager
		addListener: function( el, eventName, fn )
		{
			el = Ext.getDom(el);
			if( el && fn )
			{
				if( eventName == UNLOAD )
				{
					if( unloadListeners[el.id] === undefined )
					{
						unloadListeners[el.id] = [];
					}
					unloadListeners[el.id].push(
					[
					    eventName, fn
					]);
					return fn;
				}
				return doAdd(el, eventName, fn, false);
			}
			return false;
		},

		// This function should ALWAYS be called from Ext.EventManager
		removeListener: function( el, eventName, fn )
		{
			el = Ext.getDom(el);
			var i, len, li, lis;
			if( el && fn )
			{
				if( eventName == UNLOAD )
				{
					if( (lis = unloadListeners[el.id]) !== undefined )
					{
						for( i = 0, len = lis.length; i < len; i++ )
						{
							if( (li = lis[i]) && li[TYPE] == eventName && li[FN] == fn )
							{
								unloadListeners[el.id].splice(i, 1);
							}
						}
					}
					return;
				}
				doRemove(el, eventName, fn, false);
			}
		},

		getTarget: function( ev )
		{
			ev = ev.browserEvent || ev;
			return this.resolveTextNode(ev.target || ev.srcElement);
		},

		resolveTextNode: Ext.isGecko ? function( node )
		{
			if( !node )
			{
				return;
			}
			// work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
			var s = HTMLElement.prototype.toString.call(node);
			if( s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]' )
			{
				return;
			}
			return node.nodeType == 3 ? node.parentNode : node;
		} : function( node )
		{
			return node && node.nodeType == 3 ? node.parentNode : node;
		},

		getRelatedTarget: function( ev )
		{
			ev = ev.browserEvent || ev;
			return this.resolveTextNode(ev.relatedTarget || (/(mouseout|mouseleave)/.test(ev.type) ? ev.toElement : /(mouseover|mouseenter)/.test(ev.type) ? ev.fromElement : null));
		},

		getPageX: function( ev )
		{
			return getPageCoord(ev, "X");
		},

		getPageY: function( ev )
		{
			return getPageCoord(ev, "Y");
		},

		getXY: function( ev )
		{
			return
			[
			    this.getPageX(ev), this.getPageY(ev)
			];
		},

		stopEvent: function( ev )
		{
			this.stopPropagation(ev);
			this.preventDefault(ev);
		},

		stopPropagation: function( ev )
		{
			ev = ev.browserEvent || ev;
			if( ev.stopPropagation )
			{
				ev.stopPropagation();
			}
			else
			{
				ev.cancelBubble = true;
			}
		},

		preventDefault: function( ev )
		{
			ev = ev.browserEvent || ev;
			if( ev.preventDefault )
			{
				ev.preventDefault();
			}
			else
			{
				if( ev.keyCode )
				{
					ev.keyCode = 0;
				}
				ev.returnValue = false;
			}
		},

		getEvent: function( e )
		{
			e = e || win.event;
			if( !e )
			{
				var c = this.getEvent.caller;
				while(c)
				{
					e = c.arguments[0];
					if( e && Event == e.constructor )
					{
						break;
					}
					c = c.caller;
				}
			}
			return e;
		},

		getCharCode: function( ev )
		{
			ev = ev.browserEvent || ev;
			return ev.charCode || ev.keyCode || 0;
		},

		// clearCache: function() {},
		// deprecated, call from EventManager
		getListeners: function( el, eventName )
		{
			Ext.EventManager.getListeners(el, eventName);
		},

		// deprecated, call from EventManager
		purgeElement: function( el, recurse, eventName )
		{
			Ext.EventManager.purgeElement(el, recurse, eventName);
		},

		_load: function( e )
		{
			loadComplete = true;

			if( Ext.isIE9m && e !== true )
			{
				// IE8 complains that _load is null or not an object
				// so lets remove self via arguments.callee
				doRemove(win, "load", arguments.callee);
			}
		},

		_unload: function( e )
		{
			var EU = Ext.lib.Event, i, v, ul, id, len, scope;

			for( id in unloadListeners )
			{
				ul = unloadListeners[id];
				for( i = 0, len = ul.length; i < len; i++ )
				{
					v = ul[i];
					if( v )
					{
						try
						{
							scope = v[ADJ_SCOPE] ? (v[ADJ_SCOPE] === true ? v[OBJ] : v[ADJ_SCOPE]) : win;
							v[FN].call(scope, EU.getEvent(e), v[OBJ]);
						}
						catch (ex)
						{
						}
					}
				}
			};

			Ext.EventManager._unload();

			doRemove(win, UNLOAD, EU._unload);
		}
	};

	// Initialize stuff.
	pub.on = pub.addListener;
	pub.un = pub.removeListener;
	if( doc && doc.body )
	{
		pub._load(true);
	}
	else
	{
		doAdd(win, "load", pub._load);
	}
	doAdd(win, UNLOAD, pub._unload);
	_tryPreloadAttach();

	return pub;
}();
